package com.example.rouroudate.util

import android.annotation.SuppressLint
import android.content.ContentProvider
import android.content.ContentValues
import android.content.Context
import android.content.SharedPreferences
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.os.ParcelFileDescriptor
import android.provider.MediaStore
import android.util.Log
import android.widget.Toast
import androidx.annotation.RequiresApi
import com.example.rouroudate.R
import com.example.rouroudate.ui.gallery.ui.DateSettingData
import com.example.rouroudate.ui.gallery.ui.StyleAnimationView
import com.example.rouroudate.ui.home.ui.CircleDate
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.io.*

object PrefManager {

    private const val name = "rou_rou_date"

    private const val dateKey = "date_key_list"

    private const val DateSettingKey = "date_key_setting"

    private const val BornSettingKey = "born_key_setting"

    private const val StyleTypeKey = "style_type_key"

    private const val TAG = "PrefManager"

    private val instance: SharedPreferences by lazy {
        ApplicationUtils.instance.getSharedPreferences(name, Context.MODE_PRIVATE)
    }

    fun setStyleType(type: Int): Boolean {
        val edit = instance.edit()
        edit.putInt(StyleTypeKey, type)
        return edit.commit()
    }

    fun getStyleType(): Int {
        return instance.getInt(StyleTypeKey, StyleAnimationView.CircleType)
    }

    fun setDateSetting(dateSettingData: DateSettingData): Boolean {
        val edit = instance.edit()
        edit.putString(DateSettingKey, Gson().toJson(dateSettingData))
        return edit.commit()
    }

    fun getDateSetting(): DateSettingData {
        val data = instance.getString(DateSettingKey, null)?: return DateSettingData(26, 5)
        return Gson().fromJson(data, DateSettingData::class.java)
    }

    fun setBornSetting(circleDate: CircleDate): Boolean {
        val edit = instance.edit()
        edit.putString(BornSettingKey, Gson().toJson(circleDate))
        return edit.commit()
    }

    fun getBornSetting(): CircleDate {
        val data = instance.getString(BornSettingKey, null)?: return CircleDate()
        return Gson().fromJson(data, CircleDate::class.java)
    }

    const val SUCCESS = 100
    const val FAIL = 200

    fun setStartDate(date: CircleDate): Int {
        val str = instance.getString(dateKey, null)
        val type = object : TypeToken<HashMap<String, CircleDate>>(){}.type
        val dateMap = str?.let {
            Gson().fromJson(it, type) as HashMap<String, CircleDate>
        }?: HashMap()
        dateMap[date.getStr()] = date
        val data = Gson().toJson(dateMap)
        return if (instance.edit().putString(dateKey, data).commit()) {
            SUCCESS
        } else {
            FAIL
        }
    }

    fun delStartDate(date: CircleDate): Int {
        val str = instance.getString(dateKey, null)
        val type = object : TypeToken<HashMap<String, CircleDate>>(){}.type
        val dateMap = str?.let {
            Gson().fromJson(it, type) as HashMap<String, CircleDate>
        }?: HashMap()
        dateMap.remove(date.getStr())
        val data = Gson().toJson(dateMap)
        return if (instance.edit().putString(dateKey, data).commit()) {
            SUCCESS
        } else {
            FAIL
        }
    }


    const val SortTypeDown = 2
    const val SortTypeUp = 1
    /**
     * type = 1 sort UP
     * type = 2 sort DOWN
     * @param sortType Int
     * @return ArrayList<CircleDate>
     */
    fun getStartDateArray(sortType: Int): ArrayList<CircleDate> {
        val str = instance.getString(dateKey, null) ?: return ArrayList()
        val type = object : TypeToken<HashMap<String, CircleDate>>(){}.type
        val dateMap = Gson().fromJson(str, type) as HashMap<String, CircleDate>
        val result = ArrayList(dateMap.values)
        when(sortType) {
            SortTypeDown -> result.sortByDescending { it.beginDate.time.time }
            else -> result.sortBy { it.beginDate.time.time }
        }
        for ((i, v) in result.withIndex()) {
            if (i + 1 < result.size) {
                result[i].settingData.dateSize = TimeUtils.getDateSize(v, result[i + 1])
            }
        }
        return result
    }

    fun saveFile() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
            val root = Environment.getExternalStorageDirectory()
            val filePath = root.absolutePath + File.separator + "/Download/RouDate/" + name
            val file = File(filePath)
            file.outputStream().write(instance.all.toString().byteInputStream().readBytes())
            if (file.exists()) {
                ToastUtils.show(CompatUtils.getString(R.string.save_success))
            } else {
                ToastUtils.show(CompatUtils.getString(R.string.save_fail))
            }
        } else {
            val uri = getFileUri() ?: kotlin.run {
                val cv = ContentValues()
                cv.put(MediaStore.Files.FileColumns.DISPLAY_NAME, name)
                cv.put(MediaStore.Files.FileColumns.RELATIVE_PATH,  "Download/RouDate")
                cv.put(MediaStore.Files.FileColumns.MIME_TYPE, "text/xml")
                ApplicationUtils.instance.contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, cv)
            }
            uri?.let {
                ApplicationUtils.instance.contentResolver.openOutputStream(it)?.let { out ->
                    out.write(Gson().toJson(instance.all).toByteArray())
                    out.close()
                    ToastUtils.show(CompatUtils.getString(R.string.save_success))
                    return
                }
            }
            ToastUtils.show(CompatUtils.getString(R.string.save_fail))
        }
    }

    @SuppressLint("Recycle")
    @RequiresApi(Build.VERSION_CODES.Q)
    fun getFileUri(): Uri? {
        var uri: Uri? = null
        val downloadUri = MediaStore.Downloads.EXTERNAL_CONTENT_URI
        val dirname = "WeiXin"
        val cursor = ApplicationUtils.instance.contentResolver.query(
            downloadUri,
            null,
            MediaStore.Files.FileColumns.BUCKET_DISPLAY_NAME  + "=?",
            arrayOf(dirname),
            null
        )
        if (cursor != null) {
            val fileIdIndex = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID)
            while (cursor.moveToNext()) {
                val fileId = cursor.getLong(fileIdIndex)
                uri = downloadUri.buildUpon().appendPath("$fileId").build()
            }
        }
        return uri
    }

    @SuppressLint("Range")
    fun importFile() {
        var inputStream: InputStream? = null
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
            val root = Environment.getExternalStorageDirectory()
            val filePath = root.absolutePath + File.separator + "/Download/RouDate/" + name
            val file = File(filePath)
            file.outputStream().write(instance.all.toString().byteInputStream().readBytes())
            if (file.exists()) {
                inputStream = file.inputStream()
            }
        } else {
            getFileUri()?.let {
                ApplicationUtils.instance.contentResolver.openInputStream(it)?.let { input ->
                    inputStream = input
                }
            }
        }
        inputStream?.let {
            val type = object : TypeToken<HashMap<String, String>>() {}.type
            val dateMap = Gson().fromJson(it.reader(), type) as HashMap<String, String>
            val list = mutableListOf(DateSettingKey, BornSettingKey, StyleTypeKey, dateKey)
            for (key in list) {
                dateMap.getOrDefault(key, null)?.let { v ->
                    when (key) {
                        DateSettingKey -> setDateSetting(
                            Gson().fromJson(
                                v,
                                DateSettingData::class.java
                            )
                        )
                        BornSettingKey -> setBornSetting(
                            Gson().fromJson(
                                v,
                                CircleDate::class.java
                            )
                        )
                        StyleTypeKey -> setStyleType(v.toInt())
                        else -> instance.edit().putString(dateKey, v).apply()
                    }
                }
            }
            ToastUtils.show(CompatUtils.getString(R.string.import_success))
            return
        }
        ToastUtils.show(CompatUtils.getString(R.string.file_not_exist))
    }

    fun deleteStartDate() {
        instance.edit().remove(dateKey).apply()
    }
}